/* Copyright 2023-2025 www.flowlong.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.flowlong.bpm.engine.access.transaction;

import com.flowlong.bpm.engine.FlowLongException;
import com.flowlong.bpm.engine.assist.Assert;
import com.flowlong.bpm.engine.assist.StringUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 事务拦截器，用于产生业务逻辑类的代理类
 *
 * @author hubin
 * @since 1.0
 */
@Slf4j
public abstract class TransactionInterceptor implements MethodInterceptor {
    /**
     * 需要拦截的事务方法集合
     */
    private static final List<String> txMethods = new ArrayList<String>();

    static {
        txMethods.add("start*");
        txMethods.add("execute*");
        txMethods.add("finish*");
        txMethods.add("terminate*");
        txMethods.add("take*");
        txMethods.add("create*");
        txMethods.add("save*");
        txMethods.add("delete*");
        txMethods.add("remove*");
        txMethods.add("update*");
        txMethods.add("deploy*");
        txMethods.add("undeploy*");
        txMethods.add("redeploy*");
        txMethods.add("complete*");
        txMethods.add("assign*");
        txMethods.add("withdraw*");
        txMethods.add("reject*");
        txMethods.add("add*");
        txMethods.add("cascade*");
        txMethods.add("get*");
    }

    /**
     * 使用Cglib产生业务类的代理
     *
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Enhancer.create(clazz, this);
    }

    /**
     * 方法执行的拦截器，拦截条件为：
     * 1、数据库访问类是DBTransaction的实现类
     * 2、方法名称匹配初始化的事务方法列表
     */
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        TransactionStatus status = null;
        if (isMatch(method.getName())) {
            if (log.isDebugEnabled()) {
                log.debug("intercept method is[name=" + method.getName() + "]");
            }
            try {
                status = getTransaction();
                Assert.notNull(status);
                //调用具体无事务支持的业务逻辑
                result = proxy.invokeSuper(obj, args);
                //如果整个执行过程无异常抛出，则提交TransactionStatus持有的transaction对象
                if (status.isNewTransaction()) {
                    commit(status);
                }
            } catch (Exception e) {
                rollback(status);
                throw new FlowLongException(e);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("****don't intercept method is[name=" + method.getName() + "]");
            }
            result = proxy.invokeSuper(obj, args);
        }
        return result;
    }

    /**
     * 根据方法名称，匹配所有初始化的需要事务拦截的方法
     *
     * @param methodName
     * @return
     */
    private boolean isMatch(String methodName) {
        for (String pattern : txMethods) {
            if (StringUtils.simpleMatch(pattern, methodName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 初始化事务拦截器，设置访问对象
     *
     * @param accessObject
     */
    public abstract void initialize(Object accessObject);

    /**
     * 返回当前事务状态
     *
     * @return
     */
    protected abstract TransactionStatus getTransaction();

    /**
     * 提交当前事务状态
     *
     * @param status
     */
    protected abstract void commit(TransactionStatus status);

    /**
     * 回滚当前事务状态
     *
     * @param status
     */
    protected abstract void rollback(TransactionStatus status);
}
